package pe.com.ztaxi.taxis.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRXlsAbstractExporterParameter;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import pe.com.ztaxi.taxis.model.OrdenServicio;
import pe.com.ztaxi.taxis.model.Usuario;
import pe.com.ztaxi.taxis.service.JasperReportsService;
import pe.com.ztaxi.taxis.service.OrdenServicioService;
import pe.com.ztaxi.taxis.util.OrdenServicioJasperDTO;

@Service
public class JasperReportsServiceImpl implements JasperReportsService {

	@Autowired
	private OrdenServicioService ordenServicioService;
	
	private static final Logger LOGGER = LoggerFactory
			.getLogger(JasperReportsServiceImpl.class);

	public static final String TEMPLATE_SIMPLE = "/listaSimpleOrdenes.jrxml";
	public static final String TEMPLATE_HISTORIAL = "/listaHistorialOrdenes.jrxml";
	public static final String MEDIA_TYPE_EXCEL = "application/vnd.ms-excel";	
	public static final String MEDIA_TYPE_PDF = "application/pdf";	
	public static final String EXTENSION_TYPE_EXCEL = "xls";	
	public static final String EXTENSION_TYPE_PDF = "pdf";

	public void downloadRecientes(String type, HttpServletResponse response, Usuario usuario) {
		 
		try {
			// 1. Add report parameters
			HashMap<String, Object> params = new HashMap<String, Object>(); 
			params.put("Title", "Reporte ZSIGET recientes");			 
			// 2.  Retrieve template
			InputStream reportStream = this.getClass().getResourceAsStream(TEMPLATE_SIMPLE); 			 
			// 3. Convert template to JasperDesign
			JasperDesign jd = JRXmlLoader.load(reportStream);			 
			// 4. Compile design to JasperReport
			JasperReport jr = JasperCompileManager.compileReport(jd);			 
			// 5. Create the JasperPrint object
			// Make sure to pass the JasperReport, report parameters, and data source
			JasperPrint jp = JasperFillManager.fillReport(jr, params, getDataSourceRecientes(usuario));			 
			// 6. Create an output byte stream where data will be written
			ByteArrayOutputStream baos = new ByteArrayOutputStream();			 
			// 7. Export report
			export(type, jp, response, baos);			 
			// 8. Write to reponse stream
			write(response, baos);
		
		} catch (JRException jre) {
			LOGGER.error("Unable to process download");
			throw new RuntimeException(jre);
		}
	}
	
	public void downloadHistorial(String type, HttpServletResponse response, Usuario usuario, List<OrdenServicio> ordenes) {
		 
		try {
			// 1. Add report parameters
			HashMap<String, Object> params = new HashMap<String, Object>(); 
			params.put("Title", "Reporte ZSIGET historial");			 
			// 2.  Retrieve template
			InputStream reportStream = this.getClass().getResourceAsStream(TEMPLATE_HISTORIAL); 			 
			// 3. Convert template to JasperDesign
			JasperDesign jd = JRXmlLoader.load(reportStream);			 
			// 4. Compile design to JasperReport
			JasperReport jr = JasperCompileManager.compileReport(jd);			 
			// 5. Create the JasperPrint object
			// Make sure to pass the JasperReport, report parameters, and data source
			JasperPrint jp = JasperFillManager.fillReport(jr, params, getDataSourceHistorial(usuario, ordenes));			 
			// 6. Create an output byte stream where data will be written
			ByteArrayOutputStream baos = new ByteArrayOutputStream();			 
			// 7. Export report
			export(type, jp, response, baos);			 
			// 8. Write to reponse stream
			write(response, baos);
		
		} catch (JRException jre) {
			LOGGER.error("Unable to process download");
			throw new RuntimeException(jre);
		}
	}
	
	private HttpServletResponse export(String type, 
			JasperPrint jp, 
			HttpServletResponse response,
			ByteArrayOutputStream baos) {
		
		if (type.equalsIgnoreCase(EXTENSION_TYPE_EXCEL)) {
			// Export to output stream
			exportXls(jp, baos);
			 
			// Set our response properties
			// Here you can declare a custom filename
			String fileName = "ReporteOS.xls";
			response.setHeader("Content-Disposition", "inline; filename=" + fileName);
			
			// Set content type
			response.setContentType(MEDIA_TYPE_EXCEL);
			response.setContentLength(baos.size());
			
			return response;
		}
		
		if (type.equalsIgnoreCase(EXTENSION_TYPE_PDF)) {
			// Export to output stream
			exportPdf(jp, baos);
			 
			// Set our response properties
			// Here you can declare a custom filename
			String fileName = "ReporteOS.pdf";
			response.setHeader("Content-Disposition", "inline; filename="+ fileName);
			
			// Set content type
			response.setContentType(MEDIA_TYPE_PDF);
			response.setContentLength(baos.size());
			
			return response;
			
		} 
		
		throw new RuntimeException("No type set for type " + type);
	}
	
	private void exportXls(JasperPrint jp, ByteArrayOutputStream baos) {
		// Create a JRXlsExporter instance
		JRXlsExporter exporter = new JRXlsExporter();
		 
		// Here we assign the parameters jp and baos to the exporter
		exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
		exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
		 
		// Excel specific parameters
		exporter.setParameter(JRXlsAbstractExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
		exporter.setParameter(JRXlsAbstractExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
		exporter.setParameter(JRXlsAbstractExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
		exporter.setParameter(JRXlsAbstractExporterParameter.IS_IGNORE_GRAPHICS, Boolean.TRUE);
		exporter.setParameter(JRXlsAbstractExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_COLUMNS, Boolean.TRUE);
		 
		try {
			exporter.exportReport();
			
		} catch (JRException e) {
			throw new RuntimeException(e);
		}
	}
	
	private void exportPdf(JasperPrint jp, ByteArrayOutputStream baos) {
		// Create a JRXlsExporter instance
		JRPdfExporter exporter = new JRPdfExporter();
		 
		// Here we assign the parameters jp and baos to the exporter
		exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
		exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
		 
		try {
			exporter.exportReport();
			
		} catch (JRException e) {
			throw new RuntimeException(e);
		}
	}


	private JRDataSource getDataSourceRecientes(Usuario usuario) {
		List<OrdenServicio> ordenes;
		if(usuario.getEmpresa() != null){
			ordenes = ordenServicioService.buscarOrdenesPendientesEnProceso(usuario.getEmpresa());
		} else {
			ordenes = ordenServicioService.buscarOrdenesPendientesEnProceso();
		} 
		List<OrdenServicioJasperDTO> dtos = new ArrayList<OrdenServicioJasperDTO>();
		
		// Map records
		for (OrdenServicio orden: ordenes) {
			OrdenServicioJasperDTO dto = new OrdenServicioJasperDTO();
			dto.setCargo(orden.getSolicitante().getPerfil().getDescripcion());
			dto.setCelular(orden.getPasajero().getTodosTelefonos());
			if(orden.getUnidadMovil() == null){
				dto.setConductor("No asignado");
			} else {
				dto.setConductor(orden.getUnidadMovil().getConductor().getNombreCompleto());
			}
			if(orden.getDestinos().size() != 0){
				dto.setDestino(orden.getDestinoUltimo().getPuntoDestino().getZona().getNombre() + " " + orden.getDestinoUltimo().getPuntoDestino().getDireccion());
			}			
			dto.setEstado(orden.getEstado());
			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
			dto.setFecha(sdf.format(orden.getFechaServicio()));			
			sdf = new SimpleDateFormat("HH:mm");
			dto.setHora(sdf.format(orden.getFechaServicio()));
			sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
			dto.setFechaHoy(sdf.format(new Date()));
			if(usuario.getEmpresa() != null){
				dto.setNombreEmpresa(usuario.getEmpresa().getRazonSocial());
			} else {
				dto.setNombreEmpresa("Z-Taxi");
			}			
			dto.setNombreUsuario(usuario.getLogin());
			if(orden.getObservacion() == null){
				dto.setObservacion("");
			} else {
				dto.setObservacion(orden.getObservacion());
			}			
			dto.setOrigen(orden.getPuntoOrigen().getZona().getNombre() + " " + orden.getPuntoOrigen().getDireccion());
			dto.setPasajero(orden.getPasajero().getNombreCompleto());
			dto.setReserva(orden.getNumReserva());
			dto.setSolicitante(orden.getSolicitante().getLogin());
			dto.setTipoServicio(orden.getTipoServicio());
			dto.setVia(orden.getMedioReserva());			
			dtos.add(dto);
		}
		// Return wrapped collection
		return new JRBeanCollectionDataSource(dtos);
	}
	
	private JRDataSource getDataSourceHistorial(Usuario usuario, List<OrdenServicio> ordenes) {
		List<OrdenServicioJasperDTO> dtos = new ArrayList<OrdenServicioJasperDTO>();
		
		// Map records
		for (OrdenServicio orden: ordenes) {
			OrdenServicioJasperDTO dto = new OrdenServicioJasperDTO();
			dto.setCargo(orden.getSolicitante().getPerfil().getDescripcion());
			dto.setCelular(orden.getPasajero().getTodosTelefonos());
			if(orden.getUnidadMovil() == null){
				dto.setConductor("No asignado");
			} else {
				dto.setConductor(orden.getUnidadMovil().getConductor().getNombreCompleto());
			}	
			if(orden.getDestinos().size() != 0){
				dto.setDestino(orden.getDestinoUltimo().getPuntoDestino().getZona().getNombre() + " " + orden.getDestinoUltimo().getPuntoDestino().getDireccion());
			}
			dto.setEstado(orden.getEstado());
			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
			dto.setFecha(sdf.format(orden.getFechaServicio()));
			if(orden.getEstadoCuentaEmpresa() != null && orden.getEstadoCuentaEmpresa().getFechaCierre() != null){
				dto.setFechaCierre(sdf.format(orden.getEstadoCuentaEmpresa().getFechaCierre()));
			}
			if(orden.getFechaPago() != null){
				dto.setFechaPago(sdf.format(orden.getFechaPago()));
			}			
			sdf = new SimpleDateFormat("HH:mm");
			dto.setHora(sdf.format(orden.getFechaServicio()));
			sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
			dto.setFechaHoy(sdf.format(new Date()));
			if(usuario.getEmpresa() != null){
				dto.setNombreEmpresa(usuario.getEmpresa().getRazonSocial());
			} else {
				dto.setNombreEmpresa("Z-Taxi");
			}			
			dto.setNombreUsuario(usuario.getLogin());		
			dto.setOrigen(orden.getPuntoOrigen().getZona().getNombre() + " " + orden.getPuntoOrigen().getDireccion());
			dto.setPasajero(orden.getPasajero().getNombreCompleto());
			dto.setReserva(orden.getNumReserva());
			dto.setCentroCosto(orden.getCentroCosto().getNombre());
			dto.setSolicitante(orden.getSolicitante().getLogin());
			dto.setTipoServicio(orden.getTipoServicio());
			dto.setTipoVehiculo(orden.getTipoVehiculo());
			dto.setVia(orden.getMedioReserva());
			if(orden.getTipoPago() == "Contado"){
				dto.setContado(orden.getTotalServicio().toString());
			} else {
				dto.setCredito(orden.getTotalServicio().toString());
			}
			dtos.add(dto);
		}
		// Return wrapped collection
		return new JRBeanCollectionDataSource(dtos);
	}
	
	private void write(HttpServletResponse response,
			ByteArrayOutputStream baos) {
		 
		try {
			LOGGER.debug("Leyendo: " + baos.size());
			
			// Retrieve output stream
			ServletOutputStream outputStream = response.getOutputStream();
			// Write to output stream
			baos.writeTo(outputStream);
			// Flush the stream
			outputStream.flush();			
			
		} catch (Exception e) {
			LOGGER.error("Unable to write report to the output stream");
			throw new RuntimeException(e);
		}
	}
}
